package me.ree.zkproduct.util;

import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;

@Slf4j
public class ReflectionUtil {
    private static final Reflections reflections;

    static {
        reflections = new Reflections(
                new ConfigurationBuilder().addUrls(ClasspathHelper.forPackage("me.ree.zkproduct"))
                        .addScanners(new MethodAnnotationsScanner(), new SubTypesScanner(), new TypeAnnotationsScanner()));
    }

    public static <T> Set<Class<? extends T>> scannerSubType(Class<T> subType) {
        return reflections.getSubTypesOf(subType);
    }

    public static Set<Class<?>> getTypesAnnotatedWith(Class<? extends Annotation> annotation) {
        return reflections.getTypesAnnotatedWith(annotation);
    }

    public static Set<Method> getMethodAnnotatedWith(Class<? extends Annotation> annotation) {
        return reflections.getMethodsAnnotatedWith(annotation);
    }

    public static <T> Set<T> newInstanceFromPackage(Class<T> subType) {
        Set<Class<? extends T>> classSet = scannerSubType(subType);
        Set<T> instanceSet = new HashSet<>();
        if (classSet == null || classSet.size() <= 0) {
            return instanceSet;
        }
        for (Class<? extends T> tClass : classSet) {
            if (Modifier.isAbstract(tClass.getModifiers())) {
                continue;
            }
            try {
                instanceSet.add(tClass.newInstance());
            } catch (Exception e) {
                log.error("Cannot new an instance for " + tClass.getName(), e);
            }
        }
        return instanceSet;
    }
}
